package online.shiming.file.util;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.PutObjectRequest;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import online.shiming.file.config.OSSConfig;

/**
 * @author ming
 */
@Component
@Data
@Slf4j
public class FileWithProgress {

    @Value("${oss.bucket-name}")
    private String bucketName;
    private static String key = getKey();

    @Resource
    private OSSConfig config;

    /**
     * The uploading progress listener. Its progressChanged API is called by the SDK when there's an update.
     */
    static class PutObjectProgressListener implements ProgressListener {

        private long bytesWritten = 0;
        private long totalBytes = -1;
        private boolean succeed = false;
        private HttpSession session;
        private int percent = 0;

        private Map<String,Object> map =new HashMap<>();

        //构造方法中加入session
        public PutObjectProgressListener() {
        }

        public PutObjectProgressListener(HttpSession mSession) {
            this.session = mSession;
        }

        @Override
        public void progressChanged(ProgressEvent progressEvent) {
            long bytes = progressEvent.getBytes();
            ProgressEventType eventType = progressEvent.getEventType();
            switch (eventType) {
                case TRANSFER_STARTED_EVENT:
                    System.out.println("Start to upload......");
                    break;

                case REQUEST_CONTENT_LENGTH_EVENT:
                    this.totalBytes = bytes;
                    System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
                    break;

                case REQUEST_BYTE_TRANSFER_EVENT:
                    this.bytesWritten += bytes;
                    if (this.totalBytes != -1) {
                        percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);
                        map.put("fileSize",totalBytes);
                        map.put("percent",percent);
                        FileWebSocket.sendMessage(map);
                        System.out.println(bytes + " bytes have been written at this time, upload progress: " +
                                percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
                    } else {
                        System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" +
                                "(" + this.bytesWritten + "/...)");
                    }
                    break;

                case TRANSFER_COMPLETED_EVENT:
                    this.succeed = true;
                    System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
                    break;

                case TRANSFER_FAILED_EVENT:
                    System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
                    break;

                default:
                    break;
            }
        }

        public boolean isSucceed() {
            return succeed;
        }
    }

    public static String getKey() {
        LocalDate date = LocalDate.now();
        String[] params = date.toString().split("-");
        key = params[0] + "/" + params[1] + "/" + params[2] + "/";
        return key;
    }

    public boolean upload(File file, String fileName, HttpServletRequest request) {
        try {
            config.client().putObject(new PutObjectRequest(bucketName,
                    key + fileName,
                    file).<PutObjectRequest>withProgressListener(new
                    PutObjectProgressListener(request.getSession())));
        } finally {
            config.client().shutdown();
        }
        return true;
    }


    public boolean upload(MultipartFile multipartFile) {
        try {
            config.client().putObject(new PutObjectRequest(bucketName,
                    key + multipartFile.getOriginalFilename(),
                    multipartFile.getInputStream()).<PutObjectRequest>withProgressListener(new
                    PutObjectProgressListener()));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            config.client().shutdown();
        }
        return true;
    }

    public DataDo<?> files(String prefix) {
        ObjectListing objectListing = config.client().listObjects(bucketName, prefix);
        List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
        List<OSSDo> ossDoList = sums.stream().map(e -> {
            OSSDo ossDo = new OSSDo();
            BeanUtils.copyProperties(e, ossDo);
            return ossDo;
        }).collect(Collectors.toList());

        // 设置年月日父节点
        ossDoList.forEach(OSSDo::setDate);
        OSSDo.year.stream().sorted((a, b) -> Integer.parseInt(b) - Integer.parseInt(a)).forEach(e -> {
            ossDoList.add(new OSSDo().setId(e).setName(e).setParentId("0")
                    .setSize((Long) OSSDo.dateData.get(e + "size"))
                    .setLastModified((Date) OSSDo.dateData.get(e + "lastModified")));
        });
        OSSDo.month.stream().sorted().forEach(e -> {
            String[] date = e.split("/");
            ossDoList.add(new OSSDo().setId(date[0] + date[1])
                    .setName(date[1])
                    .setParentId(date[0])
                    .setSize((Long) OSSDo.dateData.get(date[0] + date[1] + "size"))
                    .setLastModified((Date) OSSDo.dateData.get(date[0] + date[1] + "lastModified")));
        });
        OSSDo.day.stream().sorted().forEach(e -> {
            String[] date = e.split("/");
            ossDoList.add(new OSSDo().setId(date[0] + date[1] + date[2])
                    .setName(date[2])
                    .setParentId(date[0] + date[1])
                    .setSize((Long) OSSDo.dateData.get(date[0] + date[1] + date[2] + "size"))
                    .setLastModified((Date) OSSDo.dateData.get(date[0] + date[1] + date[2] + "lastModified")));
        });
        OSSDo.year.clear();
        OSSDo.month.clear();
        OSSDo.day.clear();
        OSSDo.dateData.clear();
        return DataDo.ok(ossDoList);
    }

    public DataDo<?> getFile(String key) {
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        return DataDo.ok(config.client().generatePresignedUrl(bucketName, key, expiration));
    }
}

